#ifndef __BLOCKQUEUE_HPP__
#define __BLOCKQUEUE_HPP__

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>

using namespace std;

/**
 * @brief 模板类 BlockQueue 实现了一个线程安全的阻塞队列
 * 
 * 该阻塞队列用于生产者-消费者模式，支持多线程环境下的数据传输
 * 当队列为空时，消费者线程会被阻塞；当队列满时，生产者线程会被阻塞
 * 
 * @tparam T 队列中存储的元素类型
 */
template<typename T>
class BlockQueue
{
private:
    /**
     * @brief 检查队列是否已满
     * @return true 队列已满
     * @return false 队列未满
     */
    bool IsFull()
    {
        return _block_queue.size() == _cap;
    }

    /**
     * @brief 检查队列是否为空
     * @return true 队列为空
     * @return false 队列不为空
     */
    bool IsEmpty()
    {
        return _block_queue.empty();
    }

public:
    /**
     * @brief 构造函数，初始化阻塞队列
     * 
     * 使用方法：BlockQueue<int> queue(10); // 创建一个容量为10的整型阻塞队列
     * 
     * @param cap 队列的最大容量
     */
    BlockQueue(int cap) : _cap(cap)
    {
        pthread_mutex_init(&_mutex, nullptr);         // 初始化互斥锁
        pthread_cond_init(&_product_cond, nullptr);   // 初始化生产者条件变量
        pthread_cond_init(&_consumer_cond, nullptr);  // 初始化消费者条件变量
    }

    /**
     * @brief 入队操作，生产者向队列中添加元素
     * 
     * 使用方法：
     * BlockQueue<int> queue(5);
     * int data = 100;
     * queue.Enqueue(data); // 将data添加到队列中
     * 
     * 注意事项：
     * - 如果队列已满，此函数会阻塞直到队列有空间
     * - 此操作是线程安全的
     * 
     * @param in 要添加到队列中的元素（引用传递）
     */
    void Enqueue(T& in) // 生产者接口
    {
        pthread_mutex_lock(&_mutex);  // 加锁，保证线程安全
        while (IsFull())
        {
            // 生产者等待数量增加
            _product_wait_num++;
            // 队列已满，生产者等待
            pthread_cond_wait(&_product_cond, &_mutex);  // 等待消费者消费，同时释放锁
            // 生产者等待数量减少
            _product_wait_num--;
        }
        // 生产数据
        std::cout << "Productor:" << in << std::endl;
        _block_queue.push(in);  // 将元素添加到队列
        // 通知消费者有新数据可以消费
        if (_consumer_wait_num > 0)
        {
            pthread_cond_signal(&_consumer_cond);
        }
        pthread_mutex_unlock(&_mutex);  // 解锁
    }

    /**
     * @brief 出队操作，消费者从队列中取出元素
     * 
     * 使用方法：
     * BlockQueue<int> queue(5);
     * int data;
     * queue.Pop(&data); // 从队列中取出元素并存储到data变量
     * 
     * 注意事项：
     * - 如果队列为空，此函数会阻塞直到队列中有元素
     * - 此操作是线程安全的
     * 
     * @param out 指向存储取出元素的指针
     */
    void Pop(T *out) // 消费者接口
    {
        pthread_mutex_lock(&_mutex);  // 加锁，保证线程安全
        while (IsEmpty())
        {
            // 消费者等待数量增加
            _consumer_wait_num++;
            // 队列为空，消费者等待
            pthread_cond_wait(&_consumer_cond, &_mutex);  // 等待生产者生产，同时释放锁
            // 消费者等待数量减少
            _consumer_wait_num--;
        }
        // 消费数据
        //std::cout << "Consumer:" << _block_queue.front() << std::endl;
        *out = _block_queue.front();  // 获取队列头部元素
        _block_queue.pop();  // 移除队列头部元素
        // 通知生产者可以继续生产
        if (_product_wait_num > 0)
        {
            pthread_cond_signal(&_product_cond);
        }
        pthread_mutex_unlock(&_mutex);  // 解锁
    }

    /**
     * @brief 析构函数，清理资源
     * 
     * 释放互斥锁和条件变量等资源
     */
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);         // 销毁互斥锁
        pthread_cond_destroy(&_product_cond);   // 销毁生产者条件变量
        pthread_cond_destroy(&_consumer_cond);  // 销毁消费者条件变量
    }

private:
    std::queue<T> _block_queue;      // 内部存储队列
    int _cap;                        // 队列容量上限
    pthread_mutex_t _mutex;          // 互斥锁，保证线程安全
    pthread_cond_t _product_cond;    // 生产者条件变量，用于队列满时阻塞生产者
    pthread_cond_t _consumer_cond;   // 消费者条件变量，用于队列空时阻塞消费者

    int _product_wait_num = 0;  // 生产者等待数量
    int _consumer_wait_num = 0;  // 消费者等待数量
};

#endif
